#pragma once

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "err.hpp"
#include "Thread.hpp"
#include "ringQueue.hpp"
#include "lockGuard.hpp"

const static uint16_t default_port = 8080;

std::string transactionString(const std::string& request) {
    char c;
    std::string response;
    for (auto &r : request) {
        if (islower(r)) {
            c = toupper(r);
            response.push_back(c);
        } else {
            response.push_back(r);
        }
    }
    return response;
}

static bool isPass(const std::string& command) {
    bool pass = true;
    auto pos = command.find("rm"); 
    if (pos != std::string::npos) pass = false;
    pos = command.find("mv"); 
    if (pos != std::string::npos) pass = false;
    return pass;
}

std::string excuteCommand(const std::string& command) {
    // 1.安全检查
    if (!isPass(command)) return "you are bad man";
    // 2.业务逻辑处理
    FILE *fp = popen(command.c_str(), "r");
    if (fp == nullptr) return "None";
    // 3.获取结果
    char line[1024];
    std::string result;
    while (fgets(line, sizeof(line), fp) != NULL) {
        result += line;
    }
    pclose(fp);
    return result;
}

namespace ns_server
{
    using func_t = std::function<std::string(const std::string&)>;
    class Udp_Server {
        public:
            Udp_Server(uint16_t port) :_port(port){
                std::cout << _port << std::endl;
                pthread_mutex_init(&_lock, nullptr);
                _c = new Thread(1, std::bind(&Udp_Server::Recv, this));
                _p = new Thread(1, std::bind(&Udp_Server::Broadcast, this));
            }
            void Start() {
                // 1.创建socket接口，打开网络文件
                _sock = socket(AF_INET, SOCK_DGRAM, 0);
                if (_sock < 0) {
                    std::cerr << "create socket error: " << strerror(errno) << std::endl;
                    exit(SOCKET_ERR);
                }
                std::cout << "create socket success: " << _sock << std::endl;

                // 2.给服务器指明IP地址和Port端口号
                struct sockaddr_in local;
                bzero(&local, sizeof(local));

                local.sin_family = AF_INET;
                local.sin_port = htons(_port);
                // 不能强制类型转换
                local.sin_addr.s_addr = INADDR_ANY;     // bind主机上的任意ip
                if (bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0) {
                    std::cerr << "bind socket error: " << strerror(errno) << std::endl;
                    exit(BIND_ERR);
                }
                std::cout << "bind socket success: " << _sock << std::endl;

                _p->run();
                _c->run();
            }
            void addUser(const std::string &name, const struct sockaddr_in &peer) {
                // _onlineuser[name] = peer;
                LockGuard lockguard(&_lock);
                auto iter = _onlineuser.find(name);
                if (iter != _onlineuser.end()) return;
                else _onlineuser.insert(std::make_pair(name, peer));
            }
            void Recv() {
                char buffer[1024];
                while (true) {
                    struct sockaddr_in peer;
                    socklen_t len = sizeof(peer);
                    // 收
                    int n = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
                    if (n > 0) buffer[n] = '\0';
                    else continue;

                    // 提取client信息
                    std::string clientip = inet_ntoa(peer.sin_addr);
                    uint16_t clientport = ntohs(peer.sin_port);

                    std::cout << clientip << "-" << clientport << "# " << buffer << std::endl;
                    std::string name = clientip;
                    name += "-";
                    name += std::to_string(clientport);

                    addUser(name, peer);
                    std::string message = name + ">> " + buffer;
                    _rq.push(message);

                    // std::string response = transactionString(buffer);
                    // std::string response = _func(buffer);

                    // 发
                    // sendto(_sock, response.c_str(), response.size(), 0, (struct sockaddr*)&peer, sizeof(peer));
                }
            }
            void Broadcast() {
                while (true) {
                    std::string sendstring;
                    _rq.pop(&sendstring);
                    std::vector<struct sockaddr_in> v;
                    {
                        LockGuard lockguard(&_lock);
                        for (auto user : _onlineuser) {
                            v.push_back(user.second);
                        }
                    }
                    for (auto user : v) {
                        //std::cout << "Broadcast message to " << user.first << std::endl; 
                        sendto(_sock, sendstring.c_str(), sendstring.size(), 0, (struct sockaddr*)&(user), sizeof(user));
                    }
                }
            }
            // void Broadcast() {
            //     std::string sendstring;
            //     _rq.pop(&sendstring);
            //     LockGuard lockguard(&_lock);
            //     for (auto user : _onlineuser) {
            //         //std::cout << "Broadcast message to " << user.first << std::endl; 
            //         sendto(_sock, sendstring.c_str(), sendstring.size(), 0, (struct sockaddr*)&(user.second), sizeof(user.second));
            //     }
            // }
            ~Udp_Server() {
                pthread_mutex_destroy(&_lock);
                _c->join();
                _p->join();
                delete _c;
                delete _p;
            } 
        private:
            int _sock;
            uint16_t _port;
            // func_t _func;
            std::unordered_map<std::string, struct sockaddr_in> _onlineuser;
            RingQueue<std::string> _rq;
            pthread_mutex_t _lock;
            Thread *_c;
            Thread *_p;
    };
}